/*
 ============================================================================
 Name        : Freq_Analyzer.c
 Author      : Stefan Nagel
 Version     : 0.0.1
 Copyright   : 2013 Stefan Nagel
 	 	 	 	This file is part of zeitmessanlage.
				zeitmessanlage is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
				zeitmessanlage is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
				You should have received a copy of the GNU General Public License along with zeitmessanlage. If not, see http://www.gnu.org/licenses/.
  Description : This is the code to measure a frequency on an GNUBLIN port or use the PC simulation
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <time.h>
#include <string.h>

#include "main.h"
#include "Freq_Analyzer.h"
#include "Utilities.h"

#if VERSION == PC_SIMULATION
/*
 * PC-Test-Version des Frequeneingangs:
 * Frequenz gefunden falls "start" auf Kommandozeile eingegeben wurde
 */
int Frequency_Analyzer(void)
{
	char buff[80];
	struct timespec time[NUMBER_OF_EDGES];

	gets(buff);
	if(strcmp(buff,"start") != 0)
	{
		printf("Wrong Frequency! \n");
		return 0;

	}
	else
	{
		printf("Frequency detected! \n");
		clock_gettime(CLOCK_REALTIME, &time[0]);
		tsStartTime = time[0];
		return 1;
	}
}

#elif VERSION == GNUBLIN

int Frequency_Analyzer(void)
{
	/* These variables are for the configuration */
	int		iFrequencyInput;
	float	fExpectedFrequency;
	float	fFrequencyError;

	/* These variables are to fetch the data */
	char	pcFrequencyGpio[128];
	int		fdFrequencyGpio;
	struct	pollfd *pfdFrequencyGpio;
	int		iEdgeCounter;

	/* These variables are to fetch the data */
	struct timespec time[NUMBER_OF_EDGES];
	struct timespec Difference;
	float Difference_ms,Difference_ms_accu;
	float MeanErrorAbs, MeanErrorRel;

	/* These are just temp variables */
	char	pcBuffer[2];

	iFrequencyInput		= DEFAULT_FREQUENCY_GPIO;
	fExpectedFrequency	= DEFAULT_FREQUENCY;
	fFrequencyError		= DEFAULT_FREQUENCY_ERROR;

	sprintf(pcFrequencyGpio							 ,"/sys/class/gpio/gpio");
	sprintf(pcFrequencyGpio + strlen(pcFrequencyGpio),"%d/", iFrequencyInput);
	sprintf(pcFrequencyGpio + strlen(pcFrequencyGpio),"value");


	fdFrequencyGpio = open(pcFrequencyGpio, O_RDWR);
	if(fdFrequencyGpio < 0)
	{
		printf("Error opening file: %s",pcFrequencyGpio);
		exit(0);
	}

	/* initialize the pollfd structure */
	pfdFrequencyGpio			= malloc(sizeof(*pfdFrequencyGpio));
	pfdFrequencyGpio->fd		= fdFrequencyGpio;
	pfdFrequencyGpio->events	= POLLPRI;
	pfdFrequencyGpio->revents	= 0;



	/* start poll method with 60 seconds timeout
	 * use -1 for endless timeout
	 */
	iEdgeCounter = 0;
	while(iEdgeCounter < NUMBER_OF_EDGES)
	{
	/* call read systemcall on file
	 * this has to be called before poll
	 */
		read(fdFrequencyGpio, pcBuffer, 1);
		poll(pfdFrequencyGpio, NUMBER_OF_STREAMS, POLLING_TIMEOUT);
		clock_gettime(CLOCK_REALTIME, &time[iEdgeCounter]);
		iEdgeCounter ++;
	}

	close(fdFrequencyGpio);

	Difference_ms_accu = 0.0;

	for(iEdgeCounter=1; iEdgeCounter<NUMBER_OF_EDGES; iEdgeCounter++)
	{
		Difference = Diff_TimeSpec( time[iEdgeCounter-1],time[iEdgeCounter]);
		Difference_ms = (float)Difference.tv_sec*1000.0 + (float)Difference.tv_nsec/1000000;
		Difference_ms *= SCALE_FACTOR;
		Difference_ms_accu += Difference_ms;
#ifdef DEBUG
		printf("Counter: %d, Period: %2.4f ms, Error: %2.4f ms\n",iEdgeCounter, Difference_ms, Difference_ms-(1000.0/fExpectedFrequency));
#endif
	}
	MeanErrorAbs = Difference_ms_accu/(NUMBER_OF_EDGES-1.0)-(1000.0/fExpectedFrequency);
#ifdef DEBUG
	MeanErrorRel = (MeanErrorAbs)/(1000.0/fExpectedFrequency);
	printf("Average: Period: %2.4f ms, Error: %2.4f ms %2.4f %% \n",Difference_ms_accu/(NUMBER_OF_EDGES-1.0),MeanErrorAbs, MeanErrorRel*100.0);
#endif
	MeanErrorAbs = 1000.0*(NUMBER_OF_EDGES-1.0)/Difference_ms_accu- fExpectedFrequency;
#ifdef DEBUG
	MeanErrorRel = (MeanErrorAbs)/(fExpectedFrequency);
	printf("Average: Frequency: %2.4f kHz, Error: %2.4f Hz %2.4f %% \n\n\n",(NUMBER_OF_EDGES-1.0)/Difference_ms_accu,MeanErrorAbs, MeanErrorRel*100.0);
#endif

	if((MeanErrorAbs > fFrequencyError) || (-MeanErrorAbs > fFrequencyError))
	{
		printf("FrequencyError of %3f was to large (%3f)",(1000.0/MeanErrorAbs),fFrequencyError);
		return(0);
	}
	else
	{
		tsStartTime = time[0];
		return(1);
	}
}

#else
	#error VERSION must be either GNUBLIN or PC_SIMULATION
#endif
